<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>响应式</title>
</head>
<body>
    <script>
        /* var o = {
            name:'tom',
            age:18,
            sex:'nan'
        } */
        //将数据变得更加层次化
        let data = {
            name: 'Tom',
            age:19,
            sex:[
                {'张三':'男'},
                {'李四':'男'},
                {'王五':'男'},
            ]
        }

        function defineReactive(target,key,value,enumerable){
            //判断value值类型，入时非数组的引用类型，就要进行递归
            if(typeof value === 'object' && value != null && !Array.isArray(value)){
                reacity(value)
            }

            Object.defineProperty(target,key,{
                configurable:true,
                enumerable: !!enumerable,
                get(){  //当访问目标对象的属性时就会触发
                    console.log(`读取了${target}的属性${key}`)
                    return value
                },
                set(newvalue){ // 当修改目标对象的某个属性时就会触发
                    console.log(`设置了${target}的${key}属性值为${newvalue}`)
                    value = newvalue
                }
            })
        }
        /* let keys = Object.keys(o)
        for(let i = 0; i < keys.length; i++){
            defineReactive(o,keys[i],o[keys[i]],true)
        } */

        //处理数组响应式
        let ARRAT_METHODS = [
            'push',
            'pop',
            'reverse',
            'shift',
            'unshift',
            'splice',
        ]

        //创建了一个array_methods对象，让其原型继承于Array_prototype
        let array_methods = Object.create(Array.prototype)

        ARRAT_METHODS.forEach(method => {
            //调用原数组原型上处理数组的方法
            array_methods[method] = function(){
                console.log('调用的是拦截的' + method + '方法')
                //在这里要再次调用reacity来将调用数组方法传入的参数响应式化
                for(let i = 0; i < arguments.length; i++){
                    reacity(arguments[i])
                }
                let  res = Array.prototype[method].apply(this,arguments)
                return res
            }
        })

        //将o响应式化
        function reacity(o){
            let keys = Object.keys(o)
            for(let i = 0; i < keys.length; i++){
                let key = keys[i]
                let value = o[key]
                //判断传入的属性值是否为引用类型
                if(Array.isArray(value)){

                    //将数组的元素变成响应式的
                    value.__proto__ = array_methods
                    //对数组类型的数据进行处理
                    for(let j = 0; j < value.length; j++){
                        reacity(value[j])
                    }
                }else{
                    defineReactive(o,key,value,true)
                }
            }
            
        }

        reacity(data)


        /* 
        get函数的返回值是访问的对象的某个属性值
        set函数的会价格访问的对象的某个属性的属性值修改为用户修改的后的属性值
        这是就需要一个变量来存储这个属性的真正的属性值供get和set函数使用，
        由于需要做成响应式的数据对象太多，不能每个数据都调用一个变量来保存相应的属性值，
        因此可以使用闭包来解决，将这个属性值放在闭包值缓存起来，
        定义一个函数将真正的属性值作为函数的参数传进去，在这个函数内部执行Object.defineProperty（），这样
        要用到的变量就会在这个我们定义的这个函数的作用域中。
        */
    </script>
</body>
</html>